#!/usr/bin/python
"""
Module for parsing data files from the Arganaut hydrogenation reactors (TODO Brent to clarify).
Created: Benjamin Handanyan July 1, 2014
Modified: Brent Maranzano July 1, 2014 
    Moved script into function
    Created second function to read data        
"""
import csv
from numpy import zeros, array, where
from matplotlib import pyplot as plt
import matplotlib as mpl
mpl.use('Agg');
from pandas import DataFrame
from pdb import set_trace

def get_header(fname):
    """
    Get the header information from the file.
    Created: Benjamin Handanyan July 1, 2014
    Modified: Brent Maranzano August 4, 2014
    fname : string file name to parse
    return: dictionary of header information
    """

    with open(fname) as myfile:
    # Seek for keywords in file and save to dictionary
        keywords = dict(Solvent="", Catalyst="", Substrate="", Additives="",
                Notes="", RV="", Heat="", Pressurize_GasPressurize="", Wait="")
        for i, line in enumerate(myfile):
            for k in keywords.keys():
                if line.strip().find(k) == 0:
                    keywords[k] = line.replace('\t','   ').replace('\r\n','').replace('\n','')
            # Break the loop when the "Cool" line is reached.
            if line.find("Cool") == 0:
                break
            # Raise error if loop continues more than 50 lines
            elif i > 50:
                raise ImportError
    myfile.close()
    return keywords

def format_header(keywords):
    """
    Format the header according to the desires.
    """
    header = {}
    cols = ['Catalyst', 'Solvent', 'Substrate', 'Additives', 
            'Heat', 'Pressurize_GasPressurize', 'Wait']
    for h in cols:
        values = keywords[h].split()
        if len(values) < 8:
            values = [values[-1]]*8
        else:
            values = values[-8:]
        header[h] = values
    return header

def find_data(fname):
    """
    Find the starting point for the data in the file.
    Created: Brent Maranzano July 1, 2014
    fname : string file name to parse
    return : integer line number of the data header
    """
    with open(fname) as fObj:
        for linenum, linetxt in enumerate(fObj):
            if linetxt.find("[Sensor Monitor]") == 0:
                return linenum + 1

def get_data(fname, linenum):
    """
    Extract the data from the text file.
    Created: Brent Maranzano July 1, 2014
    fname : string file name to parse
    linenum: integer line numer of the data header
    return : numpy structured array of the data
    """
    with open(fname) as fObj:
        reader = csv.reader(fObj, delimiter='\t')
        file_header = [reader.next() for i in range(linenum)]
        data_header = array(reader.next())
        # Find column numbers of wanted data
        cols = [c for c, txt in enumerate(data_header) 
                if (txt.find('Time(ms)') != -1) or (txt.find('mMoles') != -1) 
                or (txt.find('Pressure') != -1) or (txt.find('Temp') != -1)]
        # Read in the data
        data = []
        for line in reader:
            if len(line) > 0:
                data.append([line[c] for c in cols])

    array_data = array(data, dtype='float64')
    # Create a structured array (not needed if they don't need much data manipulation)
    new_data = zeros(array_data.shape[0], dtype={
        'names':['time'] + ['mmol %i'%i for i in range(1,7)] \
                + ['pressure %i'%i for i in range(1,7)] \
                + ['temperature %i'%i for i in range(1,7)],
        'formats':['float32']*22})
    return array_data

def crop_data(header, data):
    """
    Crop the data to include values only after temperature and pressure
    have equilibrated to set values.
    header : dictionary of header data containing set points
    data : numpy array of reactor data
    return : numpy array of cropped reactor data
    """
    set_pres = header['Pressurize_GasPressurize'].split()[-8:]
    set_temp = header['Heat'].split()[-8:]
    pres = data[:,9:17]
    temp = data[:,17:26]
    test_pres = [where(pres[:, j] > float(set_pres[j])*0.99)[0] for j in range(8)]
    test_temp = [where(temp[:, j] > float(set_temp[j])*0.99)[0] for j in range(8)]
    first = max([p[0] for p in test_pres] + [t[0] for t in test_temp])
    last = min([p[-1] for p in test_pres] + [t[-1] for t in test_temp])
    return data[first:last,:]

def create_dataframe(fname, header, data):
    """
    Create a dataframe of the array
    """
    df = DataFrame(data)
    name = fname.split('.')[0] + '.xls'
    set_trace()
    df.to_excel(name)

def make_plot(fname, header, data):
    """
    Make a plot of the data and save it as a png for HTML display
    Created: Brent Maranzano July 2, 2014
    data : numpy array of data
    """
    colors = ['blue', 'green', 'red', 'brown', 'cyan', 'lime', 'magenta', 'yellow', 'indigo', 'orange', 'pink']
    params = {'figure.figsize':[15,15],
              'figure.subplot.left':0.1,
              'figure.subplot.right':0.95,
              'figure.subplot.top':0.8,
              'figure.subplot.bottom':0.15,
              'figure.subplot.wspace':0.3,
              'figure.subplot.hspace':0.2,
              'axes.color_cycle':colors
            }
    plt.rcParams.update(params)
    fig = plt.figure()
    fig.text(0.1,0.95, fname)
    for i, (k, v) in enumerate(header.iteritems()):
        fig.text(0.01, 0.93-i*0.02, k, color='black')
        for j, h in enumerate(v):
            fig.text(0.2+j*0.1, 0.93-i*0.02, h, color=colors[j])
    ax = []
    for i, axis in enumerate(['mMoles', 'Pressure', 'Temperature']):
        ax.append(fig.add_subplot('31%i'%(i+1)))
        ax[i].plot(data[:,0]/(1e3*60), data[:,i*8+1:(i+1)*8+1])
        ax[i].set_ylabel(axis)
    ax[2].set_xlabel('time (min)')
    #ax[2].legend(range(1,9))
    plt.draw()
    plt.show()
    fig.savefig('data.png')
    fig.close()

def run(fname):
    """
    Convenience function to run the module functions
    """
    keywords = get_header(fname)
    header = format_header(keywords)
    line = find_data(fname)
    data = get_data(fname, line)
    data = crop_data(keywords, data)
    create_dataframe(fname, header, data)
    make_plot(fname, header, data)

if __name__ == '__main__':
    """
    Read in the file data supplied as a command line argument.
    Create a plot of the data and save as a PNG. Rerun the
    figure making function every minute. Check if the
    supplied data file has changed. If the file has not
    changed over 10 minutes, terminate.
    To use:
    $python parse.py filename
    where filename is the text file containing the data

    This was inserted to automatically update, which causes
    the program to hang.
    from os import stat
    from time import sleep
    #last_mtime = int(stat(fname).st_mtime)
    #while True:
    #    for x in xrange(10):
    #        run(fname)
    #        sleep(30)
    #    if int(stat(fname).st_mtime) == last_mtime:
    #        break
    #    else:
    #        last_mtime = int(stat(fname).st_mtime)

    """
    import sys
    fname = sys.argv[1]
    run(fname)
